package com.xixi.canal.client.support;

import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.boot.context.properties.YamlPropertiesBinder;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class YmlConfigLoader {

    private static final String defaultName = "instance.yml";
    private static final String instance_prefix = "canal.instance";

    public static Map<String, String> loadConfigClassPath(String confDir, String destination, String name) {
        Map<String, String> filePathMap = new HashMap<>();

        File[] files = getFiles(confDir, destination, name);

        if (null != files) {
            for (File file : files) {
                String fileName = file.getName();
                if (!fileName.endsWith(".yml")) {
                    continue;
                }
                filePathMap.put(fileName, name + File.separator + fileName);
            }
        }
        return filePathMap;
    }

    public static Map<String, String> loadConfig(String confDir, String destination, String name) {

        Map<String, String> configMap = new HashMap<>();

        File[] files = getFiles(confDir, destination, name);
        if (null != files) {
            for (File file : files) {
                String fileName = file.getName();
                if (!fileName.endsWith(".yml")) {
                    continue;
                }
                try (InputStream in = new FileInputStream(file)) {
                    byte[] bytes = new byte[in.available()];
                    in.read(bytes);
                    String configContent = new String(bytes, StandardCharsets.UTF_8);
                    configMap.put(fileName, configContent);
                } catch (IOException e) {
                    throw new RuntimeException("Read " + destination + "mapping config: " + fileName + " error. ", e);
                }
            }
        }

        return configMap;
    }

    private static File[] getFiles(String confDir, String destination, String name) {
        String instanceYml = confDir + File.separator + destination + File.separator + name;

        File confDirFile = new File(instanceYml);
        if (!confDirFile.exists()) {
            URL url = YmlConfigLoader.class.getClassLoader().getResource("");
            confDirFile = new File(url.getPath() + destination + File.separator + name);
        }

        return confDirFile.listFiles();
    }


    public static <T> T loadYaml(Properties envProperties, String confDir, String destination, Class<T> clazz) {
        return loadYaml(envProperties, confDir, destination, clazz, defaultName);
    }

    public static <T> T loadYaml(Properties envProperties, String confDir, String destination, Class<T> clazz,
                                 String fileName) {
        YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
        String instanceYml = confDir + File.separator + destination + File.separator + fileName;
        ClassPathResource classPathResource = new ClassPathResource(instanceYml);
        if (!classPathResource.exists()) {
            URL url = YmlConfigLoader.class.getClassLoader().getResource("");
            instanceYml = File.separator + destination + File.separator + fileName;
            classPathResource = new ClassPathResource(instanceYml);
        }
        yaml.setResources(classPathResource);

        Properties properties = new Properties(envProperties);

        properties.putAll(yaml.getObject());

        String configPrefix = instance_prefix;
        ConfigProperties annotation = clazz.getAnnotation(ConfigProperties.class);
        if (annotation != null) {
            configPrefix = annotation.prefix();
        }
        T t = YamlPropertiesBinder.bindOrCreate(configPrefix, clazz, properties);

        if (t == null) {
            throw new RuntimeException("Config yaml file" + fileName + " load failed.");
        }
        return t;
    }

    public static void checkYaml(File file) {
        YamlPropertiesFactoryBean factoryBean = new YamlPropertiesFactoryBean();
        factoryBean.setResources(new FileSystemResource(file));
        factoryBean.getObject();
    }

}
